package com.summer.ioc.container;

import com.summer.ioc.bean.definition.AbstractBeanDefinition;
import com.summer.ioc.bean.definition.BeanDefinition;
import com.summer.ioc.container.util.ComponentUtil;
import com.summer.ioc.exception.BeanException;
import com.summer.ioc.util.ClassUtil;
import com.summer.ioc.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author xiaoye
 * @create 2021-04-29 14:20
 */
public abstract class AbstractIocContainer implements IocContainer{

    /**
     * 存放bean的真正容器
     */
    protected Map<String, BeanDefinition> container;

    protected void initialBeans()
    {
        var beanDefinitions = container.values();
        for (BeanDefinition beanDefinition : beanDefinitions) {
            if(beanDefinition.getScope().equals(AbstractBeanDefinition.BeanScope.SINGLETON) && !beanDefinition.isLazyLoad())
                buildBean(beanDefinition);
        }
    }

    protected void buildDependencyBeans(Set<String> beanDependencyNames)
    {
        if (CollectionUtils.hasElement(beanDependencyNames))
        {
            for (String beanDependencyName : beanDependencyNames) {

                if (getBean(beanDependencyName) == null) {
                    buildBean(container.get(beanDependencyName));
                }
            }
        }
    }

    protected abstract Object buildBean(BeanDefinition beanDefinition);

    protected Object suitType(String value, Class<?> type) {
        if (type.equals(String.class))
            return value;
        if (ClassUtil.isSynthetic(type) || ClassUtil.isWrap(type))
        {
            return suitSynthetic(value,type);
        }
        if (Number.class.isAssignableFrom(type))
        {
            return suitNumber(value,type);
        }
        if (type == Date.class)
        {
            try {
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        if (type == LocalDate.class)
        {
            return LocalDate.parse(value);
        }
        if (type == LocalTime.class)
        {
            return LocalTime.parse(value);
        }
        if (type == LocalDateTime.class)
        {
            return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
        return null;

    }

    private Object suitNumber(String value, Class<?> type) {
        if (type == BigInteger.class)
            return new BigInteger(value);
        if (type == BigDecimal.class)
            return new BigDecimal(value);
        return null;
    }

    private Object suitSynthetic(String value, Class<?> type) {
        if (type == int.class || type == Integer.class)
            return Integer.valueOf(value);
        if (type == short.class || type == Short.class)
            return Short.valueOf(value);
        if (type == byte.class || type == Byte.class)
            return Byte.valueOf(value);
        if (type == long.class || type == Long.class)
            return Long.valueOf(value);
        if (type == float.class || type == Float.class)
            return Float.valueOf(value);
        if (type == double.class || type == Double.class)
            return Double.valueOf(value);
        if (type == boolean.class || type == Boolean.class)
            return Boolean.valueOf(value);
        if (type == char.class || type == Character.class)
            return value.charAt(0);
        return null;
    }

    @Override
    public Object getBean(String name) {
        var bd = (AbstractBeanDefinition)container.get(name);

        return getBean(bd);
    }

    protected Object getBean(BeanDefinition bd)
    {
        if (bd == null)
            return null;
        if (bd.isLazyLoad() && bd.getObject() == null)
            return buildBean(bd);
        if (bd.getScope().equals(AbstractBeanDefinition.BeanScope.PROTOTYPE))
            return buildBean(bd);
        return bd.getObject();
    }

    @Override
    public <T> T getBean(String name, Class<T> beanClass) {
        return (T)getBean(name);
    }

    @Override
    public <T> T getBean(Class<T> beanClass) {
        var beanDefinitions = container.values();
        T bean = null;
        int count = 0;
        for (var beanDefinition : beanDefinitions) {
            if (beanClass.isAssignableFrom(beanDefinition.getBeanClass()))
            {
                bean = (T)getBean(beanDefinition);
                ++count;
            }
        }
        if (count > 1)
            throw new BeanException("[" + beanClass.getName() + "] has more than one bean matched");
        return bean;
    }

    @Override
    public void registryBean(BeanDefinition beanDefinition) {
        if (container == null)
            container = new HashMap<>();
        container.put(beanDefinition.getName(),beanDefinition);
    }

    protected void scanComponent(String[] basePackages)
    {
        ComponentUtil.scanComponent(basePackages,this);
    }
}
